home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 176-200 / disk_197 / stevie / cmdline.c < prev    next >
C/C++ Source or Header  |  1992-05-06  |  15KB  |  714 lines

  1. /*
  2.  * STEVIE - Simply Try this Editor for VI Enthusiasts
  3.  *
  4.  * Code Contributions By : Tim Thompson           twitch!tjt
  5.  *                         Tony Andrews           onecom!wldrdg!tony 
  6.  *                         G. R. (Fred) Walter    watmath!watcgl!grwalter 
  7.  */
  8.  
  9. #include "stevie.h"
  10.  
  11. static char    *altfile = NULL;    /* alternate file */
  12. static int      altline;    /* line # in alternate file */
  13.  
  14. static char    *nowrtmsg = "No write since last change (use ! to override)";
  15.  
  16. extern char   **files;        /* used for "n" and "rew" */
  17. extern int      curfile;
  18. extern int      numfiles;
  19.  
  20. /*
  21.  * The next two variables contain the bounds of any range given in a command.
  22.  * If no range was given, both contain null line pointers. If only a single
  23.  * line was given, u_pos will contain a null line pointer. 
  24.  */
  25. static LPtr     l_pos, u_pos;
  26.  
  27. static bool_t   interactive;    /* TRUE if we're reading a real command line */
  28.  
  29. static bool_t   doecmd();
  30. static void
  31. badcmd(), doshell(), get_range();
  32. static LPtr    *get_line();
  33.  
  34. #ifdef    MEGAMAX
  35. overlay "cmdline"
  36. #endif
  37.  
  38. /*
  39.  * readcmdline() - accept a command line starting with ':', '/', or '?' 
  40.  *
  41.  * readcmdline() accepts and processes colon commands and searches. If
  42.  * 'cmdline' is null, the command line is read here. Otherwise, cmdline
  43.  * points to a complete command line that should be used. This is used in
  44.  * main() to handle initialization commands in the environment variable
  45.  * "EXINIT". 
  46.  */
  47. void
  48. readcmdline(firstc, cmdline)
  49.     char            firstc;    /* either ':', '/', or '?' */
  50.     char           *cmdline;    /* optional command string */
  51. {
  52.     char            c;
  53.     char            buff[CMDBUFFSIZE];
  54.     char            cmdbuf[CMDBUFFSIZE];
  55.     char            argbuf[CMDBUFFSIZE];
  56.     char           *p, *q, *cmd, *arg;
  57.     bool_t          literal_next_flag = FALSE;
  58.  
  59.     /*
  60.      * Clear the range variables. 
  61.      */
  62.     l_pos.linep = (LINE *) NULL;
  63.     u_pos.linep = (LINE *) NULL;
  64.  
  65.     interactive = (cmdline == NULL);
  66.  
  67.     if (interactive)
  68.     gotocmdline(YES, firstc);
  69.     p = buff;
  70.     if (firstc != ':')
  71.     *p++ = firstc;
  72.  
  73.     if (interactive) {
  74.     /* collect the command string, handling '\b' and @ */
  75.     for (;;) {
  76.         c = vgetc();
  77.         if (c == CTRL('V') && !literal_next_flag) {
  78.         literal_next_flag = TRUE;
  79.         outchar('^');
  80.         continue;
  81.         }
  82.         if (c == '\n' || ((c == '\r' || c == ESC) && (!literal_next_flag)))
  83.         break;
  84.         if ((c == '\b') && (!literal_next_flag)) {
  85.         if (p > buff + (firstc != ':')) {
  86.             p--;
  87.             /*
  88.              * this is gross, but it relies only on 'gotocmdline' 
  89.              */
  90.             gotocmdline(YES, firstc == ':' ? ':' : NUL);
  91.             for (q = buff; q < p; q++)
  92.             outstr(chars[*q].ch_str);
  93.         } else {
  94.             msg("");
  95.             return;    /* back to cmd mode */
  96.         }
  97.         continue;
  98.         }
  99.         if ((c == '@') && (!literal_next_flag)) {
  100.         p = buff;
  101.         gotocmdline(YES, firstc);
  102.         continue;
  103.         }
  104.         if (literal_next_flag) {
  105.         literal_next_flag = FALSE;
  106.         outchar('\b');
  107.         }
  108.         outstr(chars[c].ch_str);
  109.         *p++ = c;
  110.     }
  111.     *p = '\0';
  112.     } else {
  113.     if (strlen(cmdline) > CMDBUFFSIZE - 2)    /* should really do something
  114.                          * better here... */
  115.         return;
  116.     strcpy(p, cmdline);
  117.     }
  118.  
  119.     /* skip any initial white space */
  120.     for (cmd = buff; *cmd != NUL && isspace(*cmd); cmd++);
  121.  
  122.     /* search commands */
  123.     c = *cmd;
  124.     if (c == '/' || c == '?') {
  125.     cmd++;
  126.     /* was the command was '//' or '??' (I.E. repeat last search) */
  127.     if ((*cmd == c) || (*cmd == NUL)) {
  128.         if (c == '/')
  129.         searchagain(FORWARD);
  130.         else
  131.         searchagain(BACKWARD);
  132.         return;
  133.     }
  134.     /* If there is a matching '/' or '?' at the end, toss it */
  135.     p = strchr(cmd, NUL);
  136.     if (*(p - 1) == c && *(p - 2) != '\\')
  137.         *(p - 1) = NUL;
  138.     dosearch((c == '/') ? FORWARD : BACKWARD, cmd);
  139.     return;
  140.     }
  141.     /*
  142.      * Parse a range, if present (and update the cmd pointer). 
  143.      */
  144.     get_range(&cmd);
  145.     if (l_pos.linep != NULL) {
  146.     if (LINEOF(&l_pos) > LINEOF(&u_pos)) {
  147.         emsg("Invalid range");
  148.         return;
  149.     }
  150.     }
  151.     strcpy(cmdbuf, cmd);    /* save the unmodified command */
  152.  
  153.     /* isolate the command and find any argument */
  154.     for (p = cmd; *p != NUL && !isspace(*p); p++);
  155.     if (*p == NUL)
  156.     arg = NULL;
  157.     else {
  158.     *p = NUL;
  159.     for (p++; *p != NUL && isspace(*p); p++);
  160.     if (*p == NUL) {
  161.         arg = NULL;
  162.     } else {
  163.         strcpy(argbuf, p);
  164.         arg = argbuf;
  165.     }
  166.     }
  167.  
  168.     if (strcmp(cmd, "q!") == 0)
  169.     getout(0);
  170.     if (strcmp(cmd, "q") == 0) {
  171.     if (Changed)
  172.         emsg(nowrtmsg);
  173.     else
  174.         getout(0);
  175.     return;
  176.     }
  177.     if (strcmp(cmd, "w") == 0) {
  178.     if (arg == NULL) {
  179.         if (Filename != NULL) {
  180.         writeit(Filename, &l_pos, &u_pos);
  181.         UNCHANGED;
  182.         } else
  183.         emsg("No output file");
  184.     } else
  185.         writeit(arg, &l_pos, &u_pos);
  186.     return;
  187.     }
  188.     if (strcmp(cmd, "wq") == 0) {
  189.     if (Filename != NULL) {
  190.         if (writeit(Filename, (LPtr *) NULL, (LPtr *) NULL))
  191.         getout(0);
  192.     } else
  193.         emsg("No output file");
  194.     return;
  195.     }
  196.     if (strcmp(cmd, "x") == 0) {
  197.     if (Changed) {
  198.         if (Filename != NULL) {
  199.         if (!writeit(Filename, (LPtr *) NULL, (LPtr *) NULL))
  200.             return;
  201.         } else {
  202.         emsg("No output file");
  203.         return;
  204.         }
  205.     }
  206.     getout(0);
  207.     }
  208.     if (strcmp(cmd, "f") == 0 && arg == NULL) {
  209.     fileinfo();
  210.     return;
  211.     }
  212.     if (*cmd == 'n') {
  213.     if ((curfile + 1) < numfiles) {
  214.         /*
  215.          * stuff ":e[!] FILE\n" 
  216.          */
  217.         stuffReadbuff(":e");
  218.         if (cmd[1] == '!')
  219.         stuffReadbuff("!");
  220.         stuffReadbuff(" ");
  221.         stuffReadbuff(files[++curfile]);
  222.         stuffReadbuff("\n");
  223.     } else
  224.         emsg("No more files!");
  225.     return;
  226.     }
  227.     if (*cmd == 'p') {
  228.     if (curfile > 0) {
  229.         /*
  230.          * stuff ":e[!] FILE\n" 
  231.          */
  232.         stuffReadbuff(":e");
  233.         if (cmd[1] == '!')
  234.         stuffReadbuff("!");
  235.         stuffReadbuff(" ");
  236.         stuffReadbuff(files[--curfile]);
  237.         stuffReadbuff("\n");
  238.     } else
  239.         emsg("No more files!");
  240.     return;
  241.     }
  242.     if (strncmp(cmd, "rew", 3) == 0) {
  243.     if (numfiles <= 1)    /* nothing to rewind */
  244.         return;
  245.     curfile = 0;
  246.     /*
  247.      * stuff ":e[!] FILE\n" 
  248.      */
  249.     stuffReadbuff(":e");
  250.     if (cmd[3] == '!')
  251.         stuffReadbuff("!");
  252.     stuffReadbuff(" ");
  253.     stuffReadbuff(files[0]);
  254.     stuffReadbuff("\n");
  255.     return;
  256.     }
  257.     if (strcmp(cmd, "e") == 0 || strcmp(cmd, "e!") == 0) {
  258.     doecmd(arg, cmd[1] == '!');
  259.     return;
  260.     }
  261.     if (strcmp(cmd, "f") == 0) {
  262.     Filename = strsave(arg);
  263.     filemess("");
  264.     return;
  265.     }
  266.     if (strcmp(cmd, "r") == 0 || strcmp(cmd, ".r") == 0) {
  267.     if (arg == NULL) {
  268.         badcmd();
  269.         return;
  270.     }
  271.     if (readfile(arg, Curschar, 1)) {
  272.         emsg("Can't open file");
  273.         return;
  274.     }
  275.     updateNextscreen(NOT_VALID);
  276.     CHANGED;
  277.     return;
  278.     }
  279.     if (strcmp(cmd, ".=") == 0) {
  280.     smsg("line %d", cntllines(Filemem, Curschar));
  281.     return;
  282.     }
  283.     if (strcmp(cmd, "$=") == 0) {
  284.     smsg("%d", cntllines(Filemem, Fileend) - 1);
  285.     return;
  286.     }
  287.     if (strncmp(cmd, "ta", 2) == 0) {
  288.     dotag(arg, cmd[2] == '!');
  289.     return;
  290.     }
  291.     if (strcmp(cmd, "set") == 0) {
  292.     doset(arg, interactive);
  293.     return;
  294.     }
  295.     if (strcmp(cmd, "help") == 0) {
  296.     if (help()) {
  297.         screenclear();
  298.         updateNextscreen(NOT_VALID);
  299.     }
  300.     return;
  301.     }
  302.     if (strcmp(cmd, "version") == 0) {
  303.     extern char    *Version;
  304.  
  305.     msg(Version);
  306.     return;
  307.     }
  308.     if (strcmp(cmd, "sh") == 0) {
  309.     doshell();
  310.     return;
  311.     }
  312.     if (strncmp(cmd, "d", 1) == 0) {
  313.     LINE           *cp;
  314.     int             n;
  315.  
  316.     if (l_pos.linep == NULL)
  317.         l_pos = *Curschar;
  318.     if (u_pos.linep == NULL)
  319.         u_pos = l_pos;
  320.  
  321.     ResetBuffers();
  322.     n = RowNumber(&l_pos);
  323.     AppendPositionToUndoUndobuff(0, n);
  324.     AppendPositionToUndobuff(0, n);
  325.     if ((Filetop->linep->next == l_pos.linep) &&
  326.         (u_pos.linep->next == Fileend->linep))
  327.         AppendToUndobuff("a");
  328.     else if (u_pos.linep->next == Fileend->linep)
  329.         AppendToUndobuff("o");
  330.     else
  331.         AppendToUndobuff("O");
  332.  
  333.     n = 0;
  334.     cp = l_pos.linep;
  335.     for (; cp != NULL && cp != Fileend->linep; cp = cp->next) {
  336.         AppendToUndobuff(cp->s);
  337.         n++;
  338.         if (cp == u_pos.linep)
  339.         break;
  340.         AppendToUndobuff(NL_STR);
  341.     }
  342.     AppendToUndobuff(ESC_STR);
  343.  
  344.     if (n > 1)
  345.         AppendNumberToUndoUndobuff(n);
  346.     AppendToUndoUndobuff("dd");
  347.  
  348.     *Curschar = l_pos;
  349.     delline(n, FALSE);
  350.     updateNextscreen(NOT_VALID);
  351.     MustRedrawScreen = TRUE;/* Shouldn't have to do this. */
  352.     return;
  353.     }
  354.     if (strncmp(cmd, "s/", 2) == 0) {
  355.     dosub(&l_pos, &u_pos, cmdbuf + 1);
  356.     return;
  357.     }
  358.     if (strncmp(cmd, "g/", 2) == 0) {
  359.     doglob(&l_pos, &u_pos, cmdbuf + 1);
  360.     return;
  361.     }
  362.     /*
  363.      * If we got a line, but no command, then go to the line. 
  364.      */
  365.     if (*cmd == NUL && l_pos.linep != NULL) {
  366.     if (u_pos.linep != NULL)
  367.         *Curschar = u_pos;
  368.     else
  369.         *Curschar = l_pos;
  370.     cursupdate();
  371.     return;
  372.     }
  373.     badcmd();
  374. }
  375.  
  376. /*
  377.  * get_range - parse a range specifier 
  378.  *
  379.  * Ranges are of the form: 
  380.  *
  381.  * addr[,addr] 
  382.  *
  383.  * where 'addr' is: 
  384.  *
  385.  * %          (entire file)
  386.  * $  [+-NUM]
  387.  * 'x [+-NUM] (where x denotes a currently defined mark)
  388.  * .  [+-NUM]
  389.  * NUM 
  390.  *
  391.  * The pointer *cp is updated to point to the first character following the
  392.  * range spec. If an initial address is found, but no second, the upper bound
  393.  * is equal to the lower. 
  394.  */
  395. static void
  396. get_range(cp)
  397.     char          **cp;
  398. {
  399.     LPtr           *l;
  400.     char           *p;
  401.  
  402.     if (**cp == '%') {
  403.     l_pos.index = 0;
  404.     l_pos.linep = Filetop->linep->next;
  405.     u_pos.index = 0;
  406.     u_pos.linep = Fileend->linep->prev;
  407.     (*cp)++;
  408.     return;
  409.     }
  410.     if ((l = get_line(cp)) == NULL)
  411.     return;
  412.  
  413.     l_pos = *l;
  414.  
  415.     for (p = *cp; *p != NUL && isspace(*p); p++);
  416.  
  417.     *cp = p;
  418.  
  419.     if (*p != ',') {        /* is there another line spec ? */
  420.     u_pos = l_pos;
  421.     return;
  422.     }
  423.     *cp = ++p;
  424.  
  425.     if ((l = get_line(cp)) == NULL) {
  426.     u_pos = l_pos;
  427.     return;
  428.     }
  429.     u_pos = *l;
  430. }
  431.  
  432. static LPtr    *
  433. get_line(cp)
  434.     char          **cp;
  435. {
  436.     static LPtr     pos;
  437.     LPtr           *lp;
  438.     char           *p, c;
  439.     int             lnum;
  440.  
  441.     pos.index = 0;        /* shouldn't matter... check back later */
  442.  
  443.     p = *cp;
  444.     /*
  445.      * Determine the basic form, if present. 
  446.      */
  447.     switch (c = *p++) {
  448.  
  449.       case '$':
  450.     pos.linep = Fileend->linep->prev;
  451.     break;
  452.  
  453.       case '.':
  454.     pos.linep = Curschar->linep;
  455.     break;
  456.  
  457.       case '\'':
  458.     if ((lp = getmark(*p++)) == NULL) {
  459.         emsg("Unknown mark");
  460.         return (LPtr *) NULL;
  461.     }
  462.     pos = *lp;
  463.     break;
  464.  
  465.       case '0':
  466.       case '1':
  467.       case '2':
  468.       case '3':
  469.       case '4':
  470.       case '5':
  471.       case '6':
  472.       case '7':
  473.       case '8':
  474.       case '9':
  475.     for (lnum = c - '0'; isdigit(*p); p++)
  476.         lnum = (lnum * 10) + (*p - '0');
  477.  
  478.     if (lnum == 0)
  479.         lnum = 1;
  480.  
  481.     pos = *gotoline(lnum);
  482.     break;
  483.  
  484.       default:
  485.     return (LPtr *) NULL;
  486.     }
  487.  
  488.     while (*p != NUL && isspace(*p))
  489.     p++;
  490.  
  491.     if (*p == '-' || *p == '+') {
  492.     bool_t          neg = (*p++ == '-');
  493.  
  494.     for (lnum = 0; isdigit(*p); p++)
  495.         lnum = (lnum * 10) + (*p - '0');
  496.  
  497.     if (neg)
  498.         lnum = -lnum;
  499.  
  500.     pos = *gotoline(cntllines(Filemem, &pos) + lnum);
  501.     }
  502.     *cp = p;
  503.     return &pos;
  504. }
  505.  
  506. static void
  507. badcmd()
  508. {
  509.     if (interactive)
  510.     emsg("Unrecognized command");
  511. }
  512.  
  513. /*
  514.  * dotag(tag, force) - goto tag 
  515.  */
  516. void
  517. dotag(tag, force)
  518.     char           *tag;
  519.     bool_t          force;
  520. {
  521.     FILE           *tp, *fopen();
  522.     char            lbuf[LSIZE];
  523.     char           *fname, *str;
  524.  
  525.     if ((tp = fopen("tags", "r")) == NULL) {
  526.     emsg("Can't open tags file");
  527.     return;
  528.     }
  529.     while (fgets(lbuf, LSIZE, tp) != NULL) {
  530.  
  531.     if ((fname = strchr(lbuf, TAB)) == NULL) {
  532.         emsg("Format error in tags file");
  533.         return;
  534.     }
  535.     *fname++ = '\0';
  536.     if ((str = strchr(fname, TAB)) == NULL) {
  537.         emsg("Format error in tags file");
  538.         return;
  539.     }
  540.     *str++ = '\0';
  541.  
  542.     if (strcmp(lbuf, tag) == 0) {
  543.         if (!force && Changed) {
  544.         emsg(nowrtmsg);
  545.         return;
  546.         }
  547.         if (doecmd(fname, force)) {
  548.         stuffReadbuff(str);    /* str has \n at end */
  549.         stuffReadbuff("\007");    /* CTRL('G') */
  550.         fclose(tp);
  551.         return;
  552.         }
  553.     }
  554.     }
  555.     emsg("tag not found");
  556.     fclose(tp);
  557. }
  558.  
  559. static          bool_t
  560. doecmd(arg, force)
  561.     char           *arg;
  562.     bool_t          force;
  563. {
  564.     int             line = 1;    /* line # to go to in new file */
  565.  
  566.     if (!force && Changed) {
  567.     emsg(nowrtmsg);
  568.     return FALSE;
  569.     }
  570.     if (arg != NULL) {
  571.     /*
  572.      * First detect a ":e" on the current file. This is mainly for ":ta"
  573.      * commands where the destination is within the current file. 
  574.      */
  575.     if (Filename != NULL && strcmp(arg, Filename) == 0) {
  576.         if (!Changed || (Changed && !force))
  577.         return TRUE;
  578.     }
  579.     if (strcmp(arg, "#") == 0) {    /* alternate */
  580.         char           *s = Filename;
  581.  
  582.         if (altfile == NULL) {
  583.         emsg("No alternate file");
  584.         return FALSE;
  585.         }
  586.         Filename = altfile;
  587.         altfile = s;
  588.         line = altline;
  589.         altline = cntllines(Filemem, Curschar);
  590.     } else {
  591.         altfile = Filename;
  592.         altline = cntllines(Filemem, Curschar);
  593.         Filename = strsave(arg);
  594.     }
  595.     }
  596.     if (Filename == NULL) {
  597.     emsg("No filename");
  598.     return FALSE;
  599.     }
  600.     /* clear mem and read file */
  601.     freeall();
  602.     filealloc();
  603.     UNCHANGED;
  604.  
  605.     readfile(Filename, Filemem, 0);
  606.     *Topchar = *Curschar;
  607.     if (line != 1) {
  608.     stuffnumReadbuff(line);
  609.     stuffReadbuff("G");
  610.     }
  611.     setpcmark();
  612.     updateNextscreen(NOT_VALID);
  613.     return TRUE;
  614. }
  615.  
  616. static void
  617. doshell()
  618. {
  619.     char           *sh, *getenv();
  620.  
  621.     sh = getenv("SHELL");
  622.     if (sh == NULL) {
  623.     emsg("Shell variable not set");
  624.     return;
  625.     }
  626.     gotocmdline(YES, NUL);
  627.  
  628.     if (system(sh) < 0) {
  629.     emsg("Exec failed");
  630.     return;
  631.     }
  632.     wait_return();
  633. }
  634.  
  635. void
  636. gotocmdline(clr, firstc)
  637.     bool_t          clr;
  638.     char            firstc;
  639. {
  640.     windgoto(Rows - 1, 0);
  641.     if (clr)
  642.     outstr(T_EL);        /* clear the bottom line */
  643.     if (firstc)
  644.     outchar(firstc);
  645. }
  646.  
  647. /*
  648.  * msg(s) - displays the string 's' on the status line 
  649.  */
  650. void
  651. msg(s)
  652.     char           *s;
  653. {
  654.     gotocmdline(YES, NUL);
  655.     outstr(s);
  656. #ifdef AMIGA
  657.     flushbuf();
  658. #endif
  659. #ifdef BSD
  660.     fflush(stdout);
  661. #endif
  662. }
  663.  
  664. /* VARARGS */
  665. void
  666. smsg(s, a1, a2, a3, a4, a5, a6, a7, a8, a9)
  667.     char           *s;
  668.     int             a1, a2, a3, a4, a5, a6, a7, a8, a9;
  669. {
  670.     char            sbuf[MAX_COLUMNS + 1];
  671.  
  672.     sprintf(sbuf, s, a1, a2, a3, a4, a5, a6, a7, a8, a9);
  673.     msg(sbuf);
  674. }
  675.  
  676. /*
  677.  * emsg() - display an error message 
  678.  *
  679.  * Rings the bell, if appropriate, and calls message() to do the real work 
  680.  */
  681. void
  682. emsg(s)
  683.     char           *s;
  684. {
  685.     UndoInProgress = FALSE;
  686.     RedrawingDisabled = FALSE;
  687.  
  688.     if (P(P_EB))
  689.     beep();
  690.     outstr(T_TI);
  691.     msg(s);
  692.     outstr(T_TP);
  693. #ifdef AMIGA
  694.     flushbuf();
  695. #endif
  696. #ifdef BSD
  697.     fflush(stdout);
  698. #endif
  699. }
  700.  
  701. void
  702. wait_return()
  703. {
  704.     char            c;
  705.  
  706.     outstr("Press RETURN to continue");
  707.     do {
  708.     c = vgetc();
  709.     } while (c != '\r' && c != '\n');
  710.  
  711.     screenclear();
  712.     updateNextscreen(NOT_VALID);
  713. }
  714.